试玩 dpdk ebfp 功能
ebpf 是什么?
eBPF 是一个在内核中运行的专用指令虚拟机,支持通过 C 等高级语言编码生成 eBPF 指令码。eBPF 指令码从用户态加载到内核后支持【翻译】为【本地机器码】的形式执行,现已支持内核多个子系统的观测点,提供了几乎无限的可观测性。
ebpf 主要工作流程如下图:
dpdk 对 ebpf 的支持
dpdk 18.0 引入 ebpf,实现为 librte_bpf 库。
支持的特性如下:
- 支持 ebpf 架构(不支持 tail-pointer)
- x86_64 与 arm64 架构支持使用 JIT将 ebpf 代码编译为 native code
- 支持 ebpf 代码校验支持用户定义的辅助函数
- 支持 rx/tx 包过滤
- 支持 ebpf 代码访问 mbuf 结构
不支持的特性如下:
- cbpf
- maps
- stail-pointer calls
dpdk ebpf 的使用过程
通过注册 rx、tx 函数内的 callback 来实现过滤功能,最终控制的是返回给上层、传输给下层的匹配特定过滤规则的 mbuf。 过滤功能主要通过 epbf 指令完成。ebpf 指令的执行支持虚拟机指令模拟与 jit 两种方式。jit 会将 ebpf 指令转化为本地机器码来运行。 目前的编程方式:用 c 等高级语言编写 ebpf 过滤代码,然后使用 clang 编译为 ebpf 指令码来加载、翻译、运行。
dpdk 中上手 ebpf 的一些前置条件
- config 配置文件中使能如下配置:
RTE_LIBRTE_GRO
RTE_LIBRTE_GSO
RTE_LIBRTE_BPF
RTE_LIBRTE_BPF_ELF
RTE_TEST_PMD
RTE_ETHDEV_RXTX_CALLBACKS
- 使用 clang 编译 examples/bpf/ 目录下的文件为 ebpf 指令码
- 运行 testpmd,通过 bpf-load 命令加载第二步生成的指令码文件
使用 dpdk 示例代码上手 dpdk ebpf 框架
ebpf 过滤程序源码如下:
#include <stdint.h>
#include <net/ethernet.h>
#include <netinet/ip.h>
#include <netinet/udp.h>
#include <arpa/inet.h>
uint64_t
entry(void *pkt)
{
struct ether_header *ether_header = (void *)pkt;
if (ether_header->ether_type != htons(0x0800))
return 0;
struct iphdr *iphdr = (void *)(ether_header + 1);
if (iphdr->protocol != 17 || (iphdr->frag_off & 0x1ffff) != 0 ||
iphdr->daddr != htonl(0x1020304))
return 0;
int hlen = iphdr->ihl * 4;
struct udphdr *udphdr = (void *)iphdr + hlen;
if (udphdr->dest != htons(5000))
return 0;
return 1;
}
上述代码实现类似 tcpdump -s 1 -d 'dst 1.2.3.4 && udp && dst port 5000’ 命令行的功能,过滤出目的 ip 为 1.2.3.4 且目的端口为 5000 的 udp 报文。
上述源码摘自 dpdk 源码根目录的 examples/bpf/t1.c 文件,编译命令如下:
clang -O2 -U __GNUC__ -target bpf -c t1.c
编译成功后,运行 testpmd 程序,执行 bpf-load 命令,示例如下:
testpmd> bpf-load rx 0 0 J ./examples/bpf/t1.o
rte_bpf_elf_load(fname="./examples/bpf/t1.o", sname=".text") successfully creates 0x7f4f2d9dc000(jit={.func=0x7f4f2d9b0000,.sz=93});
0:Success
执行 bpf-unload 命令即可卸载 ebpf 规则。
dpdk ebpf 的安全性
dpdk ebpf 的实现做了如下安全性校验:
- 参数合法性校验
- ebpf 指令合法性(正确的格式、有效字段值等)校验
- 校验是否存在不可达的指令或循环
- 加载前模拟执行所有可能存在的分支中的 ebpf 指令 校验失败则终止加载!
其它的安全保障:
- ebpf jit 翻译完成后将 rte_bpf 所在的页设置为 READ ONLY
- ebpf jit 翻译后生成的 native code 存储的页设置为读与可执行
dpdk ebpf 框架的性能负载
测试方法:
- 基于 dpdk-19.11 testpmd 测试
- 编写一个直接返回 1 的 ebpf 空规则
- 使用 testpmd 加载第二步生成的 dpdk 规则
- 持续打流 100% 带宽观测性能数据
在飞腾 D2000 上使用一个两万兆网卡测试,测试确定对 512、1518 字节性能几乎没有影响,64 字节下降了不到 3%。
参考链接
https://blog.csdn.net/force_eagle/article/details/117365557
https://doc.dpdk.org/guides/testpmd_app_ug/testpmd_funcs.html#bpf-functions